//#pragma once //目前防止头文件重复包含的方案

#ifndef _NHPP_ //防止头文件重复包含

#define _NHPP_

//C++ head
#include <iostream>
using namespace std;
#include <vector>
#include <string>

//C head
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cstdlib>

//System head
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define FNAME "/home/ARMCSKGT/C++文件夹/pipe进程通信/共享内存/." //共享内存名
#define PROG_ID 0xF  //项目代号
#define PERMS 0666 //共享内存权限

const size_t fsize = 4096; //共享内存大小4096字节

vector<string> HOSTGetPipe(int n) //创建命名管道名
{
    umask(000);
    vector<string> v;
	for (int i = 1; i <= n; ++i)
	{
		char str[1024] = {0};
		snprintf(str, sizeof(str), "%s%d", "pipe", i);
		int m = mkfifo(str,0666);
        if(m == -1)
        {
            cerr << "NamePipe Creat " << i+1 << " times" << "Error!" << errno << " : " << strerror(errno) << endl;
            exit(EOF);
        }
        v.push_back(str);
	}
    return v;
}

int SERVERGetPipe(int n)
{
    int fd = 0;
    char str[1024] = {0};
    snprintf(str, sizeof(str), "%s%d", "pipe", n);
    fd = open(str,O_RDONLY);
    if(fd == -1)
    {
        cerr << "SERVER PIPE " << "ERROR!" << errno << " : " << strerror(errno) << endl;
        cout<<"连接失败!"<<endl;
        exit(EOF);  
    }
    cout<<"客户端"<<n<<"启动成功!"<<endl;
    return fd;
}

//通过ftok获取唯一的key
key_t GetKey()
{
    key_t k = ftok(FNAME,PROG_ID);
    if(k == -1)
    {
        cerr << "ftok error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return k;
}

//通过shmget创建共享内存或获取共享内存的fd - 写进程创建 
static int FetchShm(key_t key, size_t size, int shmflg)
{
    int shmid = shmget(key,size,shmflg);
    if(shmid == -1)
    {
        cerr << "shmget create error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return shmid;
}

int CreateShm(key_t key,size_t size)
{
    return FetchShm(key,size, IPC_CREAT | IPC_EXCL | PERMS); //创建共享内存权限为PERMS且为最新
}

int GetShm(key_t key,size_t size)
{
    return FetchShm(key,size, IPC_CREAT); //获取共享内存
}

//通过shmat将共享内存关联到进程
void *AttackShm(int shmid)
{
    void *shm = shmat(shmid,NULL,0); //获取shmaddr和选项暂时不用
    if(shm ==  (void *)-1)
    {
        cerr << "shmat error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return shm;
}


//通过shmdt将共享内存与进程脱离
void DetachShm(void* shm)
{
    int n = shmdt(shm);
    if(n == -1)
    {
        cerr << "shmdt error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
}

//shmctl操作当前的共享内存，删除共享内存 - 获取共享内存对象信息...
void DelShm(int shmid)
{
    int n = shmctl(shmid,IPC_RMID,NULL);
    if(n == -1)
    {
        cerr << "shmctl error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
}



//将共享内存操作整合为一个对象
template<class T>
class SHM_HOST //主机对象
{
public:
    SHM_HOST(int n) //指定创建多少个管道
    :_shmid(-1)
    ,_shm(nullptr)
    ,_p()
    {
        _shmid = CreateShm(GetKey(),fsize);
        _shm = (T*)AttackShm(_shmid);
        _p = HOSTGetPipe(n); //返回每个管道读端的fd和管道名
    }

    ~SHM_HOST()
    {
        DetachShm((void*)_shm);
        DelShm(_shmid); //删除共享内存
        for(int i = 0;i<_p.size();++i)
        {
            int fd = open(_p[i].c_str(),O_WRONLY);
            if(fd == -1)
            {
                cerr << "Open Pipe Write "<< "Error!" << errno << " : " << strerror(errno) << endl;
                exit(EOF); 
            }
            write(fd,"0",1);
            close(fd);
        }
        cout<<"等待所有服务端安全退出中...请耐心等候！"<<endl;
        sleep(1); //等待子进程退出
        for(int i = 0;i<_p.size();++i)
        {
            unlink(_p[i].c_str()); //删除文件
        }
    }

    T *operator()() //仿函数式返回地址
    {
        return _shm;
    }

    void _write(int n) //向第几个服务端写入 默认从1开始
    {
        int fd = open(_p[n-1].c_str(),O_WRONLY);
        if(fd == -1)
        {
            cerr << "Open Pipe Write "<< "Error!" << errno << " : " << strerror(errno) << endl;
            exit(EOF); 
        }
        write(fd,"1",1);
        close(fd);
    }

private:
    int _shmid;
    T *_shm;
    vector<string> _p; //存放创建的命名管道名方便删除
};

template<class T>
class SHM_SERVER //服务的读取
{
public:
    SHM_SERVER(int n) //第几个连接的服务端
    :_shmid(-1)
    ,_shm(nullptr)
    ,_pr(-1)
    {
        _shmid = GetShm(GetKey(),fsize); //获取共享内存
        _shm = (T*)AttackShm(_shmid); //链接共享内存
        _pr = SERVERGetPipe(n);
    }

    ~SHM_SERVER()
    {
        DetachShm((void*)_shm); //卸载共享内存链接
        close(_pr); //关闭管道
    }

    T *operator()() //仿函数式返回地址
    {
        return _shm;
    }

    int r_fd()
    {
        return _pr;
    }

private:
    int _shmid;
    T *_shm;
    int _pr; //管道读取fd
};

#endif